/** * Copyright (c)2010-2011 Enterprise Website Content Management System(EWCMS), All rights reserved. * EWCMS PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. * http://www.ewcms.com */ package com.ewcms.plugin.report.generate.factory; import java.io.ByteArrayOutputStream; import java.sql.Connection; import java.sql.Date; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.text.NumberFormat; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.Map.Entry; import javax.sql.DataSource; import org.jfree.chart.ChartUtilities; import org.jfree.chart.JFreeChart; import org.jfree.chart.axis.CategoryLabelPosition; import org.jfree.chart.axis.CategoryLabelPositions; import org.jfree.chart.axis.CategoryLabelWidthType; import org.jfree.chart.labels.AbstractCategoryItemLabelGenerator; import org.jfree.chart.labels.CategoryItemLabelGenerator; import org.jfree.chart.plot.CategoryPlot; import org.jfree.chart.plot.MultiplePiePlot; import org.jfree.chart.plot.PiePlot; import org.jfree.chart.plot.PiePlot3D; import org.jfree.chart.plot.Plot; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.renderer.category.CategoryItemRenderer; import org.jfree.chart.renderer.category.LineAndShapeRenderer; import org.jfree.chart.title.LegendTitle; import org.jfree.chart.urls.CategoryURLGenerator; import org.jfree.data.category.CategoryDataset; import org.jfree.data.category.DefaultCategoryDataset; import org.jfree.text.TextBlockAnchor; import org.jfree.ui.RectangleAnchor; import org.jfree.ui.RectangleEdge; import org.jfree.ui.TextAnchor; import org.jfree.util.TableOrder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.util.Assert; import com.ewcms.common.convert.ConvertException; import com.ewcms.common.convert.ConvertFactory; import com.ewcms.plugin.BaseException; import com.ewcms.plugin.externalds.generate.factory.DataSourceFactoryable; import com.ewcms.plugin.externalds.generate.factory.init.EwcmsDataSourceFactoryable; import com.ewcms.plugin.externalds.generate.service.EwcmsDataSourceServiceable; import com.ewcms.plugin.externalds.model.BaseDS; import com.ewcms.plugin.report.generate.service.chart.ChartGenerationService; import com.ewcms.plugin.report.generate.util.ParamConversionPage; import com.ewcms.plugin.report.generate.vo.PageShowParam; import com.ewcms.plugin.report.model.ChartReport; import com.ewcms.plugin.report.model.Parameter; /** * @author 吴智俊 */ @Service public class ChartFactory implements ChartFactoryable { private static final Logger logger = LoggerFactory.getLogger(ChartFactory.class); @Autowired private EwcmsDataSourceFactoryable ewcmsDataSourceFactory; @Autowired private DataSource dataSource; public void setAlqcDataSourceFactory(EwcmsDataSourceFactoryable alqcDataSourceFactory) { this.ewcmsDataSourceFactory = alqcDataSourceFactory; } public EwcmsDataSourceFactoryable getAlqcDataSourceFactory() { return this.ewcmsDataSourceFactory; } public void setDataSource(DataSource dataSource) { this.dataSource = dataSource; } /** * 生成图型报表上显示的数据 */ static class LabelGenerator extends AbstractCategoryItemLabelGenerator implements CategoryItemLabelGenerator { private static final long serialVersionUID = -4769271989622322803L; /** 阈值 */ private double threshold; /** * 创建一个新的生成器,只能显示大于或等于阈值的标签。 * * @param threshold 阈值 */ public LabelGenerator(double threshold) { super("", NumberFormat.getInstance()); this.threshold = threshold; } /** * 生成一个指定标签。标签通常是格式化的数据值,但任何文字都可以使用。 * * @param dataset 数据源 (<code>null</code> 不合法). * @param series * @param category * * @return 标签 (可能 <code>null</code>). */ public String generateLabel(CategoryDataset dataset, int series, int category) { String result = null; Number value = dataset.getValue(series, category); if (value != null) { double v = value.doubleValue(); if (v > this.threshold) { result = value.toString(); // 这里可以使用格式 } } return result; } } public byte[] export(ChartReport report, Map<String, String> pageParams) throws Exception { if (report == null){ return null; } DefaultCategoryDataset dataset = buildDataset(report, pageParams); java.awt.Font titleFont = new java.awt.Font(report.getFontName(), report.getFontStyle(), report.getFontSize()); String chartTitle = report.getChartTitle(); chartTitle = replaceParam(pageParams, report.getParameters(), chartTitle, false); String horizAxisLabel = report.getHorizAxisLabel(); horizAxisLabel = replaceParam(pageParams, report.getParameters(), horizAxisLabel, false); String vertAxisLabel = report.getVertAxisLabel(); vertAxisLabel = replaceParam(pageParams, report.getParameters(), vertAxisLabel, false); Boolean showLegend = report.getShowLegend(); Boolean showTooltips = report.getShowTooltips(); Boolean drillThroughEnabled = false; ChartReport.Type chartType = report.getType(); CategoryURLGenerator urlGenerator = null; JFreeChart chart = null; switch (chartType) { case VERTBAR: chart = ChartGenerationService.createBarChart(chartTitle, titleFont, horizAxisLabel, vertAxisLabel, dataset, PlotOrientation.VERTICAL, showLegend, showTooltips, drillThroughEnabled, urlGenerator); break; case VERTBAR3D: chart = ChartGenerationService.createBarChart3D(chartTitle, titleFont, horizAxisLabel, vertAxisLabel, dataset, PlotOrientation.VERTICAL, showLegend, showTooltips, drillThroughEnabled, urlGenerator); break; case HORIZBAR: chart = ChartGenerationService.createBarChart(chartTitle, titleFont, horizAxisLabel, vertAxisLabel, dataset, PlotOrientation.HORIZONTAL, showLegend, showTooltips, drillThroughEnabled, urlGenerator); break; case HORIZBAR3D: chart = ChartGenerationService.createBarChart3D(chartTitle, titleFont, horizAxisLabel, vertAxisLabel, dataset, PlotOrientation.HORIZONTAL, showLegend, showTooltips, drillThroughEnabled, urlGenerator); break; case STACKEDVERTBAR: chart = ChartGenerationService.createStackedBarChart(chartTitle, titleFont, horizAxisLabel, vertAxisLabel, dataset, PlotOrientation.VERTICAL, showLegend, showTooltips, drillThroughEnabled, urlGenerator); break; case STACKEDVERTBAR3D: chart = ChartGenerationService.createStackedBarChart3D(chartTitle, titleFont, horizAxisLabel, vertAxisLabel, dataset, PlotOrientation.VERTICAL, showLegend, showTooltips, drillThroughEnabled, urlGenerator); break; case STACKEDHORIZBAR: chart = ChartGenerationService.createStackedBarChart(chartTitle, titleFont, horizAxisLabel, vertAxisLabel, dataset, PlotOrientation.HORIZONTAL, showLegend, showTooltips, drillThroughEnabled, urlGenerator); break; case STACKEDHORIZBAR3D: chart = ChartGenerationService.createStackedBarChart3D(chartTitle, titleFont, horizAxisLabel, vertAxisLabel, dataset, PlotOrientation.HORIZONTAL, showLegend, showTooltips, drillThroughEnabled, urlGenerator); break; case VERTLINE: chart = ChartGenerationService.createLineChart(chartTitle, titleFont, horizAxisLabel, vertAxisLabel, dataset, PlotOrientation.VERTICAL, showLegend, showTooltips, drillThroughEnabled, urlGenerator); break; case HORIZLINE: chart = ChartGenerationService.createLineChart(chartTitle, titleFont, horizAxisLabel, vertAxisLabel, dataset, PlotOrientation.HORIZONTAL, showLegend, showTooltips, drillThroughEnabled, urlGenerator); break; case VERTAREA: chart = ChartGenerationService.createAreaChart(chartTitle, titleFont, horizAxisLabel, vertAxisLabel, dataset, PlotOrientation.VERTICAL, showLegend, showTooltips, drillThroughEnabled, urlGenerator); break; case HORIZAREA: chart = ChartGenerationService.createAreaChart(chartTitle, titleFont, horizAxisLabel, vertAxisLabel, dataset, PlotOrientation.HORIZONTAL, showLegend, showTooltips, drillThroughEnabled, urlGenerator); break; case VERTSTACKEDAREA: chart = ChartGenerationService.createStackedAreaChart(chartTitle, titleFont, horizAxisLabel, vertAxisLabel, dataset, PlotOrientation.VERTICAL, showLegend, showTooltips, drillThroughEnabled, urlGenerator); break; case HORIZSTACKEDAREA: chart = ChartGenerationService.createStackedAreaChart(chartTitle, titleFont, horizAxisLabel, vertAxisLabel, dataset, PlotOrientation.HORIZONTAL, showLegend, showTooltips, drillThroughEnabled, urlGenerator); break; case PIEBYCOLUMN: chart = ChartGenerationService.createPieChart(chartTitle, titleFont, dataset, TableOrder.BY_COLUMN, showLegend, showTooltips, drillThroughEnabled, null); break; case PIEBYROW: chart = ChartGenerationService.createPieChart(chartTitle, titleFont, dataset, TableOrder.BY_ROW, showLegend, showTooltips, drillThroughEnabled, null); break; case PIEBYCOLUMN3D: chart = ChartGenerationService.create3DPieChart(chartTitle, titleFont, dataset, TableOrder.BY_COLUMN, showLegend, showTooltips, drillThroughEnabled, null); break; case PIEBYROW3D: chart = ChartGenerationService.create3DPieChart(chartTitle, titleFont, dataset, TableOrder.BY_ROW, showLegend, showTooltips, drillThroughEnabled, null); break; default: throw new BaseException("一个未定义的图表类型", "一个未定义的图表类型"); } try { Integer bgColorR = report.getBgColorR(); Integer bgColorG = report.getBgColorG(); Integer bgColorB = report.getBgColorB(); chart.setBackgroundPaint(new java.awt.Color(bgColorR, bgColorG, bgColorB)); String axisFontName = report.getAxisFontName(); Integer axisFontStyle = report.getAxisFontStyle(); Integer axisFontSize = report.getAxisFontSize(); java.awt.Font axisFont = new java.awt.Font(axisFontName, axisFontStyle, axisFontSize); String axisTickFontName = report.getAxisTickFontName(); Integer axisTickFontStyle = report.getAxisTickFontStyle(); Integer axisTickFontSize = report.getAxisTickFontSize(); java.awt.Font axisTickFont = new java.awt.Font(axisTickFontName, axisTickFontStyle, axisTickFontSize); String legendFontName = report.getLegendFontName(); Integer legendFontStyle = report.getLegendFontStyle(); Integer legendFontSize = report.getLegendFontSize(); java.awt.Font legendFont = new java.awt.Font(legendFontName, legendFontStyle, legendFontSize); Integer tickLabelRotate = report.getTickLabelRotate(); String dataFontName = report.getDataFontName(); Integer dataFontStyle = report.getDataFontStyle(); Integer dataFontSize = report.getDataFontSize(); java.awt.Font dataFont = new java.awt.Font(dataFontName, dataFontStyle, dataFontSize); Plot plot = chart.getPlot(); if (!(plot instanceof MultiplePiePlot)) { CategoryPlot categoryPlot = chart.getCategoryPlot(); CategoryItemRenderer renderer = categoryPlot.getRenderer(); renderer.setBaseItemLabelGenerator(new LabelGenerator(0.0)); renderer.setBaseItemLabelFont(dataFont); renderer.setBaseItemLabelsVisible(true); if (chartType == ChartReport.Type.VERTLINE || chartType == ChartReport.Type.HORIZLINE) { LineAndShapeRenderer lineRenderer = (LineAndShapeRenderer) categoryPlot.getRenderer(); lineRenderer.setBaseShapesVisible(true); lineRenderer.setDrawOutlines(true); lineRenderer.setUseFillPaint(true); } } if (plot instanceof CategoryPlot) { CategoryPlot catPlot = (CategoryPlot) plot; catPlot.getDomainAxis().setLabelFont(axisFont); catPlot.getRangeAxis().setLabelFont(axisFont); catPlot.getDomainAxis().setTickLabelFont(axisTickFont); catPlot.getRangeAxis().setTickLabelFont(axisTickFont); catPlot.getDomainAxis().setMaximumCategoryLabelWidthRatio(100.0f); double angle = -2.0 * Math.PI / 360.0 * (double) tickLabelRotate; CategoryLabelPositions oldp = catPlot.getDomainAxis().getCategoryLabelPositions(); CategoryLabelPositions newp = new CategoryLabelPositions(oldp.getLabelPosition(RectangleEdge.TOP), new CategoryLabelPosition(RectangleAnchor.TOP, TextBlockAnchor.TOP_RIGHT, TextAnchor.TOP_RIGHT, angle, CategoryLabelWidthType.RANGE, 0.0f), oldp.getLabelPosition(RectangleEdge.LEFT), oldp.getLabelPosition(RectangleEdge.RIGHT)); catPlot.getDomainAxis().setCategoryLabelPositions(newp); } else if (plot instanceof PiePlot3D) { PiePlot3D piePlot = (PiePlot3D) plot; piePlot.setLabelFont(axisFont); piePlot.setDirection(org.jfree.util.Rotation.CLOCKWISE); piePlot.setForegroundAlpha(0.5f); piePlot.setNoDataMessage("无数据显示"); } else if (plot instanceof PiePlot) { PiePlot piePlot = (PiePlot) plot; piePlot.setLabelFont(axisFont); } LegendTitle legend = (LegendTitle) chart.getLegend(); if (legend != null) { legend.setItemFont(legendFont); RectangleEdge legendRectEdge = RectangleEdge.BOTTOM; Integer legendPosition = report.getLegendPosition(); switch (legendPosition) { case 0: legendRectEdge = RectangleEdge.LEFT; break; case 1: legendRectEdge = RectangleEdge.TOP; break; case 2: legendRectEdge = RectangleEdge.RIGHT; break; case 3: legendRectEdge = RectangleEdge.BOTTOM; break; } legend.setPosition(legendRectEdge); } } catch (Exception e) { logger.error("Chart Export Exception",e); } ByteArrayOutputStream out = new ByteArrayOutputStream(); ChartUtilities.writeChartAsPNG(out, chart, report.getChartWidth(), report.getChartHeight()); return out.toByteArray(); } public List<PageShowParam> chartParameters(ChartReport report) { Assert.notNull(report); Set<Parameter> paramSet = report.getParameters(); return ParamConversionPage.conversion(paramSet); } /** * 构建数据 * * @param reportDataSet 数据源 * @param sql SQL语句 * @return DefaultCategoryDataset * @throws BaseException */ @SuppressWarnings("rawtypes") private DefaultCategoryDataset buildDataset(ChartReport report, Map<String, String> pageParams) throws BaseException { DefaultCategoryDataset dataset = new DefaultCategoryDataset(); Connection con = null; EwcmsDataSourceServiceable service = null; int colCount; try { BaseDS dataSet = report.getBaseDS(); String executableSQL = report.getChartSql(); executableSQL = replaceParam(pageParams, report.getParameters(), executableSQL, true); if (dataSet == null) { con = dataSource.getConnection(); } else { DataSourceFactoryable factory = (DataSourceFactoryable) getAlqcDataSourceFactory().getBean(dataSet.getClass()); service = factory.createService(dataSet); con = service.openConnection(); } Statement st = con.createStatement(); ResultSet rs = st.executeQuery(executableSQL); colCount = rs.getMetaData().getColumnCount(); if (colCount == 2) { while (rs.next()) { try { try{ dataset.addValue(rs.getDouble(1), "", (Comparable) rs.getObject(2)); }catch(Exception e){ dataset.addValue(rs.getDouble(2), "", (Comparable) rs.getObject(1)); } // if (rs.getMetaData().getColumnType(1) == Types.NUMERIC){ // dataset.addValue(rs.getDouble(1), "", (Comparable) rs.getObject(2)); // }else if (rs.getMetaData().getColumnType(2) == Types.NUMERIC) { // dataset.addValue(rs.getDouble(2), "", (Comparable) rs.getObject(1)); // } } catch (Exception e) { logger.error("定义的SQL表达式有错误",e); throw new BaseException("定义的SQL表达式有错误","定义的SQL表达式有错误"); } } } else if (colCount == 3) { while (rs.next()) { try { // log.info(rs.getMetaData().getColumnType(1)); // log.info(rs.getMetaData().getColumnType(2)); // log.info(rs.getMetaData().getColumnType(3)); // if (rs.getMetaData().getColumnType(1) == Types.NUMERIC){ // dataset.addValue(rs.getDouble(1), (Comparable) rs.getObject(2), (Comparable) rs.getObject(3)); // }else if (rs.getMetaData().getColumnType(2) == Types.NUMERIC){ // dataset.addValue(rs.getDouble(2), (Comparable) rs.getObject(1), (Comparable) rs.getObject(3)); // }else if (rs.getMetaData().getColumnType(3) == Types.NUMERIC){ // dataset.addValue(rs.getDouble(3), (Comparable) rs.getObject(1), (Comparable) rs.getObject(2)); // } try{ dataset.addValue(rs.getDouble(3), (Comparable) rs.getObject(1), (Comparable) rs.getObject(2)); }catch(Exception e){ try{ dataset.addValue(rs.getDouble(2), (Comparable) rs.getObject(1), (Comparable) rs.getObject(3)); }catch(Exception ex){ dataset.addValue(rs.getDouble(1), (Comparable) rs.getObject(2), (Comparable) rs.getObject(3)); } } } catch (Exception e) { logger.error("定义的SQL表达式有错误", e); throw new BaseException("定义的SQL表达式有错误","定义的SQL表达式有错误"); } } } else { logger.error("SQL表达式小于1或大于2列"); throw new BaseException("图表需要1或2维的结果", "图表需要1或2维的结果"); } st.close(); rs.close(); } catch (SQLException e) { throw new BaseException(e.toString(),e.toString()); } catch (ConvertException e){ throw new BaseException(e.toString(),e.toString()); } catch (ClassNotFoundException e){ throw new BaseException(e.toString(),e.toString()); } finally { if (service != null) { service.closeConnection(); } if (con != null) { try { con.close(); } catch (SQLException e) { } con = null; } } return dataset; } /** * 用值替换参数 * * @param chartParam * @param sql * @return * @throws ConvertException * @throws ClassNotFoundException * @throws TypeHandlerException */ @SuppressWarnings("unchecked") private String replaceParam(Map<String, String> pageParams, Set<Parameter> paramSets, String expression, Boolean isSql) throws ConvertException, ClassNotFoundException { if (paramSets == null || paramSets.size() == 0) { return expression; } Map<String, Object> chartParam = new HashMap<String, Object>(); if (pageParams == null || pageParams.size() == 0){ for (Parameter param : paramSets) { String value = param.getDefaultValue(); if (value == null) { continue; } String className = param.getClassName(); Class<Object> forName = (Class<Object>)Class.forName(className); Object paramValue = ConvertFactory.instance.convertHandler(forName).parse(value); chartParam.put(param.getEnName(), paramValue); } }else{ for (Parameter param : paramSets) { String value = pageParams.get(param.getEnName()); if (value == null) { continue; } String className = param.getClassName(); Class<Object> forName = (Class<Object>)Class.forName(className); Object paramValue = ConvertFactory.instance.convertHandler(forName).parse(value); chartParam.put(param.getEnName(), paramValue); } } int beginIndex = 0; int endIndex = 0; StringBuffer sb = new StringBuffer(expression); for (int i = 0; i < sb.length(); i++) { String p = sb.substring(i, i + 1); if (p.equals("$")) { beginIndex = i; continue; } if (p.equals("}")) { endIndex = i; } if (endIndex > beginIndex) { String temp = sb.substring(beginIndex, endIndex + 1); Iterator<Entry<String, Object>> iterator = chartParam.entrySet().iterator(); while (iterator.hasNext()) { Map.Entry<String, Object> e = (Map.Entry<String, Object>) iterator.next(); if (temp.indexOf(e.getKey(), 0) != -1) { Object value = e.getValue(); if (isSql) { if (value instanceof String) { sb = sb.replace(beginIndex, endIndex + 1, "'" + value + "'"); } else if (value instanceof Date){ sb = sb.replace(beginIndex, endIndex + 1, "'" + value + "'"); } else { sb = sb.replace(beginIndex, endIndex + 1, "" + value + ""); } } else { sb = sb.replace(beginIndex, endIndex + 1, "" + value + ""); } } } beginIndex = endIndex; } } return sb.toString(); } }